/*
 * axiDma.c - Xilinx AXI DMA设备驱动
 */

#include <vxWorks.h>
#include <vsbConfig.h>
#include <intLib.h>
#include <errnoLib.h>
#include <sioLib.h>
#include <ioLib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vxAtomicLib.h>
#include <semLib.h>
#include <sysLib.h>
#include <vmLib.h>
#include <hwif/vxBus.h>
#include <hwif/buslib/vxbFdtLib.h>
#include <private/vmLibP.h>
#include <iosLib.h>
#include <cacheLib.h>
#include "axiDma.h"

#define AXI_DMA_NAME "axi_dma"

/* DMA寄存器偏移 */
#define DMA_CTRL_REG        0x00
#define DMA_STATUS_REG      0x04
#define DMA_SRC_ADDR_LOW    0x18
#define DMA_SRC_ADDR_HIGH   0x1C
#define DMA_LENGTH_REG      0x28

/* DMA控制寄存器位定义 */
#define DMA_START_BIT      (1 << 0)
#define DMA_RESET_BIT      (1 << 2)
#define DMA_INT_ENABLE_BIT (1 << 12)

typedef struct {
    VXB_DEV_ID pDev; 
    void *  fpgaBar;				/*fpga 控制器寄存器bar*/
    void *  fpgaHandle; 			/*fpga 控制器句柄*/
    PHYS_ADDR fpgaPhyBar;   // 寄存器物理地址
} DMA_DRV_CTRL;

#define FPGA_BAR(pDev)      (((DMA_DRV_CTRL *)vxbDevSoftcGet(pDev))->fpgaBar)
#define FPGA_HANDLE(pDev)   (((DMA_DRV_CTRL *)vxbDevSoftcGet(pDev))->fpgaHandle)


#undef CSR_READ_4
#define CSR_READ_4(pDev, addr)                                  \
    vxbRead32 (FPGA_HANDLE(pDev), (UINT32 *)((char *)FPGA_BAR(pDev)+((UINT32)(addr))))

#undef CSR_WRITE_4
#define CSR_WRITE_4(pDev, addr, data)                           \
    vxbWrite32 (FPGA_HANDLE(pDev),                             \
        (UINT32 *)((char *)FPGA_BAR(pDev)+((UINT32)(addr))), data)
 
 
/* VxBus驱动方法 */
LOCAL STATUS axiDmaProbe(VXB_DEV_ID);
LOCAL STATUS axiDmaAttach(VXB_DEV_ID);

LOCAL VXB_DRV_METHOD axiDmaMethods[] = {
    { VXB_DEVMETHOD_CALL(vxbDevProbe), axiDmaProbe },
    { VXB_DEVMETHOD_CALL(vxbDevAttach), axiDmaAttach },
    VXB_DEVMETHOD_END
};

VXB_DRV axiDmaDrv = {
    { NULL },
    AXI_DMA_NAME,
    "Xilinx AXI DMA Driver",
    VXB_BUSID_FDT,
    0,
    0,
    axiDmaMethods
};

/* 设备树匹配表 */
LOCAL VXB_FDT_DEV_MATCH_ENTRY axiDmaMatch[] = {
    { "xlnx,axi-dma", NULL },
    {}
};

/* DMA中断处理函数 */
LOCAL STATUS axiDmaIsr(VXB_DEV_ID pDev)
{

    return OK;
}

/* 初始化DMA控制器 */
STATUS axiDmaInit(VXB_DEV_ID pDev)
{
	UINT32 val;
    DMA_DRV_CTRL *pDrvCtrl = (DMA_DRV_CTRL *)vxbDevSoftcGet(pDev);
    if (pDrvCtrl == NULL) {
        return ERROR;
    }
		
    // 复位DMA控制器
    CSR_WRITE_4(pDev, DMA_CTRL_REG, DMA_RESET_BIT);
    vxbUsDelay(1000); // 短暂延时
    
	/* disable interrupt */
	val = CSR_READ_4(pDev, DMA_CTRL_REG);
	val = (~ 0x7000) & val;
	CSR_WRITE_4(pDev, DMA_CTRL_REG, val);
	

    return OK;
}

/* 启动DMA传输 */
STATUS axiDmaTransfer(VXB_DEV_ID pDev, UINT32 src, UINT32 length)
{
	UINT32 status, phyAddr, val, addr;
	int i = 0;
    DMA_DRV_CTRL *pDrvCtrl = (DMA_DRV_CTRL *)vxbDevSoftcGet(pDev);
    if (pDrvCtrl == NULL) {
        return ERROR;
    }
    
	/* check tx length */
	if(length < 1)
	{
		logMsg("Invalid tx length !\n",\
				1,2,3,4,5,6);
		return ERROR;
	}

	/* check if the engine is busy */
	if(!(CSR_READ_4(pDev, DMA_STATUS_REG) & 0x1))	//channel is running
	{
		while(!(CSR_READ_4(pDev, DMA_STATUS_REG) &0x2))	//running & not idle
		{
			vxbUsDelay(1);
			i++;
			if(i>1000000)
			{
				logMsg("err:adma-%d tx engine is time out!\n", 0,1,2,3,4,5);
				return ERROR;
			}
		}
	}
	/* enable MM2S channel */
	val = CSR_READ_4(pDev, DMA_CTRL_REG);
	val = val | 0x1;						//set RS bits
	CSR_WRITE_4(pDev, DMA_CTRL_REG, val);
	 
	if(CSR_READ_4(pDev, DMA_STATUS_REG) & 0x1)	// channel halted
	{
		logMsg("Failed to start engine, status 0x%x, reseting... please try again\n", 	\
				CSR_READ_4(pDev, DMA_STATUS_REG),1,2,3,4,5);
		CSR_WRITE_4(pDev, DMA_CTRL_REG, 0x4);
		vxbUsDelay(100);
		return ERROR;
	}


	/* set memory address */
	CACHE_USER_FLUSH (src, length);	/**/			//flush cache to memory
    
	VIRT_ADDR virAddr = src;
	vmTranslate(NULL, virAddr, &phyAddr);		//get phy addr
	
	addr = (UINT32)phyAddr & 0xFFFFFFFF;
	CSR_WRITE_4(pDev, DMA_SRC_ADDR_LOW, addr);
	addr = 0;
	CSR_WRITE_4(pDev, DMA_SRC_ADDR_HIGH, addr);
	

	/* start MM2S by set tx length */
	CSR_WRITE_4(pDev, DMA_LENGTH_REG, length);
	
	while(!(CSR_READ_4(pDev, DMA_STATUS_REG) &0x2))	//running & not idle
		{
			vxbUsDelay(1);
			i++;
			if(i>1000000)
			{
				logMsg( "ERR:adma%d tx engine is time out!\n", 0,1,2,3,4,4);
				return ERROR;
			}
		}
	 
    return OK;
}

/* 探测函数 */
LOCAL STATUS axiDmaProbe(VXB_DEV_ID pDev)
{
    return vxbFdtDevMatch(pDev, axiDmaMatch, NULL);
}

/* 附加函数 */
LOCAL STATUS axiDmaAttach(VXB_DEV_ID pDev)
{
    VXB_RESOURCE *pRes;
    VXB_RESOURCE_ADR *pResAdr;
    DMA_DRV_CTRL *pDrvCtrl;
    PHYS_ADDR phyAddr;

    // 分配驱动控制结构
    pDrvCtrl = vxbMemAlloc(sizeof(DMA_DRV_CTRL));
    if (pDrvCtrl == NULL) {
        return ERROR;
    }
    bzero(pDrvCtrl, sizeof(DMA_DRV_CTRL));
    vxbDevSoftcSet(pDev, pDrvCtrl);
    pDrvCtrl->pDev = pDev;

    // 获取寄存器资源
    pRes = vxbResourceAlloc(pDev, VXB_RES_MEMORY, 0);
    if (pRes == NULL) {
        free(pDrvCtrl);
        return ERROR;
    }

    pResAdr = (VXB_RESOURCE_ADR *)pRes->pRes;
    if (pResAdr == NULL)
        return ERROR;
    pDrvCtrl->fpgaBar = (void *)pResAdr->virtual;
    logMsg("AXI DMA: Virtual register base address: 0x%p\n", pDrvCtrl->fpgaBar, 0, 0, 0, 0, 0);
    taskDelay(1);
    if (vmTranslate(NULL, (VIRT_ADDR)pDrvCtrl->fpgaBar, &phyAddr) == ERROR) {
        logMsg("AXI DMA: Failed to translate virtual address to physical address\n", 0, 0, 0, 0, 0, 0);
        free(pDrvCtrl);
        return ERROR;
    }
    pDrvCtrl->fpgaPhyBar = phyAddr;
    logMsg("AXI DMA: Physical register base address: 0x%lx\n", (unsigned long)phyAddr, 0, 0, 0, 0, 0);
    taskDelay(1);
    pDrvCtrl->fpgaHandle = pResAdr->pHandle;

    // // 获取中断资源
    // pDrvCtrl->intRes = vxbResourceAlloc(pDev, VXB_RES_IRQ, 0);
    // if (pDrvCtrl->intRes == NULL) {
    //     free(pDrvCtrl);
    //     return ERROR;
    // }

    // 创建信号量
//    pDrvCtrl->dmaSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
//    if (pDrvCtrl->dmaSem == NULL) {
//        free(pDrvCtrl);
//        return ERROR;
//    }

    // // 连接中断处理函数
    // if (vxbIntConnect(pDev, pDrvCtrl->intRes, (VOIDFUNCPTR)axiDmaIsr, pDev) != OK) {
    //     free(pDrvCtrl);
    //     return ERROR;
    // }

    // // 使能中断
    // vxbIntEnable(pDev, pDrvCtrl->intRes);

    // 初始化DMA控制器
    if (axiDmaInit(pDev) != OK) {
        free(pDrvCtrl);
        return ERROR;
    }

    return OK;
}

/* 设备打开函数 */
void axiDmaOpen(VXB_DEV_ID *ppDev, UINT32 unit)
{
    static STATUS initialized = ERROR;
    
    if (initialized == ERROR) {
        initialized = vxbDrvAdd(&axiDmaDrv);
    }
    
    if (initialized == OK) {
        *ppDev = vxbDevAcquireByName(AXI_DMA_NAME, unit);
    }
}

/* 设备关闭函数 */
int axiDmaClose(VXB_DEV_ID pDev)
{
    return OK;
}
